package com.morgan.recyclerviewdemo.view

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import androidx.annotation.ColorInt
import com.morgan.recyclerviewdemo.utils.DeviceUtil

/**
 *description：
 *<br>author：caowugao
 *<br>time：2020/7/19 0019 16:52
 */
class WeatherLineView<T : WeatherLineView.ICoorinate>(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : View(context, attrs, defStyleAttr) {

    private var mWidth = 0
    private var mHeight = 0
    private var mMaxContentWidth = 0
    private var mMaxContentHeight = 0
    private var mData : List<T> = ArrayList()

    //坐标轴
    @ColorInt var mCoorinateXDescTextColor : Int = Color.RED
    var mCoorinateXDescTextSize = DeviceUtil.dp2px(context, 10f).toFloat()
    @ColorInt var mCoorinateYDescTextColor : Int = Color.RED
    var mCoorinateXColor : Int = Color.RED
    var mCoorinateXWidth = DeviceUtil.dp2px(context, 1f)

    var mCoorinateYDescTextSize  = DeviceUtil.dp2px(context, 10f).toFloat()
    var mIconWidth = DeviceUtil.dp2px(context, 10f).toFloat()
    var mIconHeight = mIconWidth

    private var mCoorinateXDescs : List<String> = ArrayList()
    private var mCoorinateYDescs : List<String> = ArrayList()
    var mCoorinateXIcons : ArrayList<Bitmap> = ArrayList()

    //折线颜色和宽度
    @ColorInt var mBrokenLineColor : Int = Color.GREEN
    var mBrokenLineWidth : Int = DeviceUtil.dp2px(context, 1f)

    var mContentBackground : Bitmap? = null

    @ColorInt var mContentHorizontalLineColor : Int = Color.WHITE
    var mContentHorizontalLineWidth : Int = DeviceUtil.dp2px(context, 1f)
    private val mPaint : Paint

    //y轴文字最大宽度
    private var mCoorinateYDescMaxWidth = 0f
    //y轴文字到y轴的距离
    private var mYDescDistanceCoorinateY = DeviceUtil.dp2px(context, 2f)
    //x轴文字到x轴的距离
    private var mXDescDistanceCoorinateX = DeviceUtil.dp2px(context, 2f)

    private val mTextBounds : Rect

    //坐标轴行高
    private var mCoorinateRowHeight  = 0

    private var mCoorinateVerticalWidth = 0

    //天气图片到x轴的距离
    private var mIconsDistanceCoorinateX = DeviceUtil.dp2px(context, 5f)

    //天气图片下边的横线宽度
    private var mIconBelowLineWidth = DeviceUtil.dp2px(context, 3f)
    private var mIconBelowLineLength = DeviceUtil.dp2px(context, 10f)
    //天气图片下边的横线颜色
    private var mIconBelowLineColor = Color.BLUE

    private val mExtraRect1 : Rect
    private val mExtraRect2 : Rect

    //y轴文字 = i个行高
    private val mCoorinateYDescMap : HashMap<String, Int>
    private val mCoorinateXDescMap : HashMap<String, Int>

    private var mYDescFirstTextHeight = 0
    private var mXDescFirstTextWidth = 0
    private val mLines = ArrayList<Point>()

    init {
        mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mTextBounds = Rect()
        mExtraRect1 = Rect()
        mExtraRect2 = Rect()
        mCoorinateYDescMap = HashMap<String, Int>()
        mCoorinateXDescMap = HashMap()
    }

    constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0) {

    }


    interface ICoorinate {

        companion object {
            fun getCoorinateX(start : Int, end : Int, step : Int) : List<String> {
                if (end <= start) {
                    return ArrayList()
                }
                val list = mutableListOf<String>()
//                val rang = start until end
//                for (i in rang step step) {
//                    list.add(if (i < 10) "0$i:00" else "$i:00")
//                }
                var i = start
                while (i < end) {
//                    list.add(if (i < 10) "0$i:00" else "$i:00")
                    list.add(formatXDesc(i))
                    i += step
                }
                i -= step
                if (i < end - 1) {
                    i = end - 1
//                    list.add(if (i < 10) "0$i:00" else "$i:00")
                    list.add(formatXDesc(i))
                }
                return list
            }

            inline fun formatXDesc(i : Int) : String = if (i < 10) "0$i:00" else "$i:00"



//            fun getCoorinateY(start : Int, end : Int, step : Int) : List<String> {
//                if (end <= start) {
//                    return ArrayList()
//                }
//                val list = mutableListOf<String>()
////                val rang = start until end
////                for (i in rang step step) {
////                    list.add("$i°")
////                }
//                var i = start
//                while (i < end) {
//                    list.add("$i°")
//                    i += step
//                }
//
//                i -= step
//                if (i < end - 1) {
//                    i = end - 1
//                    list.add("$i°")
//                }
//
//                return list
//            }
//        }
            fun getCoorinateY(start : Int, end : Int) : List<String> {
                if (end <= start) {
                    return ArrayList()
                }
                val list = mutableListOf<String>()
                val step = 5
                var i = start
                while (i < end) {
                    list.add("${getRegionDescY(i, step)}°")
                    i += step
                }
                val mayBeTheLast = getRegionDescY(i, step)
                val mayBeTheLastDesc = "$mayBeTheLast°"
                if (!list.contains(mayBeTheLastDesc)) {
                    list.add(mayBeTheLastDesc)
                }
                if (mayBeTheLast < end - 1) {
                    val theLast = getRegionDescY(i + step, step)
                    val theLastDesc = "$theLast°"
                    if (!list.contains(theLastDesc)) {
                        list.add(theLastDesc)
                    }
                }

                return list
            }

             inline fun getRegionDescY(i : Int, step : Int) : Int{
               val tmp = i/step
                return tmp * step
            }
        }

        fun getPoint() : Point
    }


    class Point(var x : Int, var y : Int){

    }

    fun setUp(data : List<T>) {
        mData = data
        tryToGetNeededData()
        invalidate()
    }

    private fun tryToGetNeededData() {
        if (mData.isEmpty()) {
            return
        }

        val first = mData[0].getPoint()
        var minX = first.x
        var maxX = first.x

        var minY = first.y
        var maxY = first.y
        for (data in mData) {

            val p = data.getPoint()
            if (p.x < minX) {
                minX = p.x
            }

            if (p.x > maxX) {
                maxX = p.x
            }

            if (p.y < minY) {
                minY = p.y
            }

            if (p.y > maxY) {
                maxY = p.y
            }
        }
        mCoorinateXDescs = ICoorinate.getCoorinateX(minX, maxX + 1, 1)
        mCoorinateYDescs = ICoorinate.getCoorinateY(minY, maxY + 1)

        mCoorinateYDescMaxWidth = getCoorinateYDescMaxWidth()

        if (mHeight != 0) {
            var mFirstYDescHeight = 0
            var mXDescHeight = 0
            var mXDescWidth = 0
            if (!mCoorinateYDescs.isEmpty()) {
                mPaint.textSize = mCoorinateYDescTextSize
                mPaint.getTextBounds(mCoorinateYDescs[0], 0, mCoorinateYDescs[0].length, mTextBounds)
            }
            mFirstYDescHeight = mTextBounds.height()

            if (!mCoorinateXDescs.isEmpty()) {
                mPaint.textSize = mCoorinateXDescTextSize
                mPaint.getTextBounds(mCoorinateXDescs[0], 0, mCoorinateXDescs[0].length, mTextBounds)
                mXDescWidth = mPaint.measureText(mCoorinateXDescs[0]).toInt()
            }
            mXDescHeight = mTextBounds.height()

            val coorinateHeight = mHeight - paddingTop - mFirstYDescHeight/2 - paddingBottom - mXDescHeight - mXDescDistanceCoorinateX
            if (!mCoorinateYDescs.isEmpty()) {
                mCoorinateRowHeight = coorinateHeight/mCoorinateYDescs.size
            }

            if (mCoorinateXDescs.size > 1) {
                mCoorinateVerticalWidth = ((mWidth - paddingLeft - mCoorinateYDescMaxWidth - mYDescDistanceCoorinateY - mXDescWidth)/(mCoorinateXDescs.size - 1)).toInt()
            }
        }
    }

    private fun getCoorinateYDescMaxWidth(): Float {
        if (mCoorinateYDescs == null || mCoorinateYDescs.isEmpty()) {
            return 0f
        }
        mPaint.textSize = mCoorinateYDescTextSize
        var maxWidth = 0f
        for (text in mCoorinateYDescs) {
            maxWidth = Math.max(mPaint.measureText(text), maxWidth)
        }
        return maxWidth
    }


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        var resultWidth = 0

        val wideMode = MeasureSpec.getMode(widthMeasureSpec)
        val wideSize = MeasureSpec.getSize(widthMeasureSpec)
        val defaultWidth = suggestedMinimumWidth
        measureContentSize()
        when (wideMode) {
            MeasureSpec.EXACTLY -> resultWidth = wideSize
            MeasureSpec.AT_MOST -> resultWidth = Math.min(wideSize, mMaxContentWidth + paddingLeft + paddingRight)
            MeasureSpec.UNSPECIFIED -> resultWidth = Math.max(defaultWidth, mMaxContentWidth)
        }

        var resultHeight = 0
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val defaultHeight = suggestedMinimumHeight
        when (heightMode) {
            MeasureSpec.EXACTLY -> resultHeight = heightSize
            MeasureSpec.AT_MOST -> resultHeight = Math.min(heightSize, mMaxContentHeight + paddingTop + paddingBottom)
            MeasureSpec.UNSPECIFIED -> resultHeight = Math.max(defaultHeight, mMaxContentHeight)
        }

        setMeasuredDimension(resultWidth, resultHeight)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        mWidth = w
        mHeight = h
//        measureItemHeight()
        tryToGetNeededData()
    }

    private fun measureContentSize(){

    }


    override fun onDraw(c: Canvas) {
        mPaint.reset()
        //画背景
        drawContentBackground(c)
        //画y轴文字以及中间的横线
        drawCoorinateYUI(c)
        //画x轴文字 和 天气图片
        drawCoorinateXUI(c)
        //画折线图
        drawBrokenLine(c)
    }

    private fun drawBrokenLine(c: Canvas) {
        if (mData == null || mData.isEmpty()) {
            return
        }
        mPaint.strokeJoin = Paint.Join.ROUND
        mPaint.color = mBrokenLineColor
        mPaint.style = Paint.Style.STROKE
        mPaint.strokeWidth = mBrokenLineWidth.toFloat()
        mLines.clear()
        mData.forEach {data ->
            val point = data.getPoint()
            val regionDescY = ICoorinate.getRegionDescY(point.y, 5)
            val rowHeightCount = mCoorinateYDescMap["$regionDescY°"]
            if (rowHeightCount != null) {
                val lineY = paddingTop + mYDescFirstTextHeight/2 + mCoorinateRowHeight * rowHeightCount
                val realY = lineY - mCoorinateRowHeight * (point.y % 5)/5
//                mXDescFirstTextWidth
                val xDesc = ICoorinate.formatXDesc(point.x)
                val coorinateVerticalWidthCount = mCoorinateXDescMap[xDesc]
                if (coorinateVerticalWidthCount != null) {
                    val realX = paddingLeft + mCoorinateYDescMaxWidth + mYDescDistanceCoorinateY + mCoorinateVerticalWidth * coorinateVerticalWidthCount + mXDescFirstTextWidth/2
                    mLines.add(WeatherLineView.Point(realX.toInt(), realY))
                }
            }
        }

        val lineSize = mLines.size
        val rang = 0..mLines.size - 1
        for (i in rang) {
            if (i + 1 < lineSize) {
                c.drawLine(mLines[i].x.toFloat(),
                    mLines[i].y.toFloat(), mLines[i + 1].x.toFloat(),
                    mLines[i + 1].y.toFloat(), mPaint)
            }
        }
        mLines.clear()
    }

    private fun drawContentBackground(c: Canvas) {
        if (mContentBackground != null) {
            if (!mCoorinateYDescs.isEmpty()) {
                mPaint.textSize = mCoorinateYDescTextSize
                mPaint.getTextBounds(
                    mCoorinateYDescs[0],
                    0,
                    mCoorinateYDescs[0].length,
                    mTextBounds
                )
            }
            mExtraRect1.set(0, 0, mContentBackground!!.width, mContentBackground!!.height)
            mExtraRect2.left =
                (paddingLeft + mCoorinateYDescMaxWidth + mYDescDistanceCoorinateY).toInt()
            mExtraRect2.top = paddingTop + mTextBounds.height() / 2
            mExtraRect2.right = mWidth - paddingRight
            mExtraRect2.bottom =
                mHeight - paddingBottom - mTextBounds.height() - mXDescDistanceCoorinateX
            c.drawBitmap(mContentBackground!!, mExtraRect1, mExtraRect2, mPaint)
        }
    }

    private fun drawCoorinateXUI(c: Canvas) {
        if (mCoorinateXDescs == null
            || mCoorinateXDescs.isEmpty()
            || mCoorinateXIcons == null
            || mCoorinateXIcons.size != mCoorinateXDescs.size ) {
            return
        }
        val paddingLeft = paddingLeft
        val paddingBottom = paddingBottom

        //画x轴文字
        val textY = (mHeight - paddingBottom).toFloat()
        var rang = 0..mCoorinateXDescs.size - 1

        val textWidth = mPaint.measureText(mCoorinateXDescs[0])
        mXDescFirstTextWidth = textWidth.toInt()
        for (i in rang) {
            //画x轴文字
            mPaint.textSize = mCoorinateXDescTextSize
            mPaint.setColor(mCoorinateXDescTextColor)
            val text = mCoorinateXDescs[i]
            mPaint.getTextBounds(text, 0, text.length, mTextBounds)
            val textX = paddingLeft + mCoorinateYDescMaxWidth + mYDescDistanceCoorinateY + mCoorinateVerticalWidth * i
            mCoorinateXDescMap[text] = i
            c.drawText(text, textX, textY, mPaint)

            //画x轴天气图片
            val bitmap = mCoorinateXIcons[i]
            val iconY = mHeight - paddingBottom - mTextBounds.height() - mXDescDistanceCoorinateX - mCoorinateXWidth - mIconsDistanceCoorinateX - mIconHeight
            val iconX = textX + textWidth/2 - mIconWidth/2
            mExtraRect1.set(0, 0, bitmap.width, bitmap.height)

            mExtraRect2.set(iconX.toInt(), iconY.toInt(), (iconX + mIconWidth).toInt(), (iconY + mIconHeight).toInt())

            c.drawBitmap(bitmap, mExtraRect1, mExtraRect2, mPaint)
//            c.drawBitmap(bitmap, iconX, iconY.toFloat(), mPaint)

            //画x轴天气图片下面的横线
            mPaint.setColor(mIconBelowLineColor)
            mExtraRect1.left = (textX + textWidth/2 - mIconBelowLineLength/2).toInt()
            mExtraRect1.top = (textY - mTextBounds.height() - mXDescDistanceCoorinateX - mIconBelowLineWidth).toInt()
            mExtraRect1.right =  mExtraRect1.left + mIconBelowLineLength
            mExtraRect1.bottom = mExtraRect1.top + mIconBelowLineWidth
            c.drawRect(mExtraRect1, mPaint)
        }
        
    }

    private fun drawCoorinateYUI(c: Canvas) {
        if (mCoorinateYDescs == null || mCoorinateYDescs.isEmpty()) {
            return
        }
        val paddingLeft = paddingLeft
        val paddingTop = paddingTop

        mPaint.textSize = mCoorinateYDescTextSize

        val rang = 0..mCoorinateYDescs.size - 1
        val coorinateYDescsSize = mCoorinateYDescs.size

        val horizontalLineX = (paddingLeft + mCoorinateYDescMaxWidth + mYDescDistanceCoorinateY).toInt()
        mExtraRect1.left = horizontalLineX
        mExtraRect1.right = mWidth - paddingRight
        for (i in rang) {
            //画y轴的文字
            mPaint.setColor(mCoorinateYDescTextColor)
            val text = mCoorinateYDescs[i]
            mPaint.getTextBounds(text, 0, text.length, mTextBounds)
            if (i == 0) {
                mYDescFirstTextHeight = mTextBounds.height()
            }
            val textY = paddingTop + mYDescFirstTextHeight/2 + mCoorinateRowHeight * (coorinateYDescsSize - 1 - i) + mTextBounds.height()/2
            mCoorinateYDescMap[text] = coorinateYDescsSize - 1 - i
            c.drawText(text, paddingLeft.toFloat(), textY.toFloat(), mPaint)

            //画中间的横线
            mPaint.setColor(mContentHorizontalLineColor)
            mExtraRect1.top = textY - mTextBounds.height()/2 - mContentHorizontalLineWidth/2
            mExtraRect1.bottom =  mExtraRect1.top + mContentHorizontalLineWidth
            c.drawRect(mExtraRect1, mPaint)
        }

        //画x轴
        mPaint.color = mCoorinateXColor
        var coorinateXDescHeight = 0
        if (mCoorinateXDescs != null && !mCoorinateXDescs.isEmpty()) {
            mPaint.textSize = mCoorinateXDescTextSize
            mPaint.getTextBounds(mCoorinateXDescs[0], 0, mCoorinateXDescs[0].length, mTextBounds)
            coorinateXDescHeight = mTextBounds.height()
        }
        mExtraRect1.top = mHeight - paddingBottom - coorinateXDescHeight - mXDescDistanceCoorinateX - mCoorinateXWidth
        mExtraRect1.bottom = mExtraRect1.top + mCoorinateXWidth
        c.drawRect(mExtraRect1, mPaint)
    }
}